this._actors = [];
this._capturedEventId = 0;
- this._keyFocusNotifyId = 0;
- this._focusWindowChangedId = 0;
this._ignoreRelease = false;
- this._isUngrabbingCount = 0;
this._modalCount = 0;
- this._grabFocusCount = 0;
},
// addActor:
grab: function(params) {
params = Params.parse(params, { actor: null,
modal: false,
- grabFocus: false,
focus: null,
onUngrab: null });
if (params.modal && !this._takeModalGrab())
return false;
- if (params.grabFocus && !this._takeFocusGrab(hadFocus))
- return false;
-
this._grabStack.push(params);
if (params.focus) {
params.focus.grab_key_focus();
- } else if (newFocus && (hadFocus || params.grabFocus)) {
+ } else if (newFocus && hadFocus) {
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
newFocus.grab_key_focus();
}
- if ((params.grabFocus || params.modal) && !this._capturedEventId)
+ if (params.modal && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
return true;
global.sync_pointer();
},
- _takeFocusGrab: function(hadFocus) {
- let firstGrab = (this._grabFocusCount == 0);
- if (firstGrab) {
- let metaDisplay = global.screen.get_display();
-
- this._grabbedFromKeynav = hadFocus;
- this._preGrabInputMode = global.stage_input_mode;
-
- if (this._preGrabInputMode == Shell.StageInputMode.NORMAL)
- global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
-
- this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
- this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
- }
-
- this._grabFocusCount++;
- return true;
- },
-
- _releaseFocusGrab: function() {
- this._grabFocusCount--;
- if (this._grabFocusCount > 0)
- return;
-
- if (this._keyFocusNotifyId > 0) {
- global.stage.disconnect(this._keyFocusNotifyId);
- this._keyFocusNotifyId = 0;
- }
-
- if (this._focusWindowChangedId > 0) {
- let metaDisplay = global.screen.get_display();
- metaDisplay.disconnect(this._focusWindowChangedId);
- this._focusWindowChangedId = 0;
- }
-
- let prePopInputMode = global.stage_input_mode;
-
- if (this._grabbedFromKeynav) {
- if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
- prePopInputMode != Shell.StageInputMode.FULLSCREEN)
- global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
- }
-
- global.screen.focus_default_window(global.display.get_current_time_roundtrip());
- },
-
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
if (grabStackIndex < 0)
return;
- // We may get key focus changes when calling onUngrab, which
- // would cause an extra ungrab() on the next actor in the
- // stack, which is wrong. Ignore key focus changes during the
- // ungrab, and restore the saved key focus ourselves afterwards.
- // We use a count as ungrab() may be re-entrant, as onUngrab()
- // may ungrab additional actors.
- this._isUngrabbingCount++;
-
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
if (poppedGrab.modal)
this._releaseModalGrab();
-
- if (poppedGrab.grabFocus)
- this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
if (poppedGrab.savedFocus)
poppedGrab.savedFocus.grab_key_focus();
}
-
- this._isUngrabbingCount--;
},
_onCapturedEvent: function(actor, event) {
return true;
}
- if (!button && this._modalCount == 0)
- return false;
-
if (this._isWithinGrabbedActor(event.get_source()))
return false;
return true;
}
- return this._modalCount > 0;
- },
-
- _onKeyFocusChanged: function() {
- if (this._isUngrabbingCount > 0)
- return;
-
- let focus = global.stage.key_focus;
- if (!focus || !this._isWithinGrabbedActor(focus))
- this.ungrab({ isUser: true });
+ return true;
},
-
- _focusWindowChanged: function() {
- let metaDisplay = global.screen.get_display();
- if (metaDisplay.focus_window != null)
- this.ungrab({ isUser: true });
- }
});
return GLib.markup_escape_text(text, -1);
}
+const FocusGrabber = new Lang.Class({
+ Name: 'FocusGrabber',
+
+ _init: function(actor) {
+ this._actor = actor;
+ this._prevKeyFocusActor = null;
+ this._focusActorChangedId = 0;
+ this._focused = false;
+ },
+
+ grabFocus: function() {
+ if (this._focused)
+ return;
+
+ this._prevFocusedWindow = global.display.focus_window;
+ this._prevKeyFocusActor = global.stage.get_key_focus();
+
+ this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
+
+ if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
+ this._actor.grab_key_focus();
+
+ this._focused = true;
+ },
+
+ _focusUngrabbed: function() {
+ if (!this._focused)
+ return false;
+
+ if (this._focusActorChangedId > 0) {
+ global.stage.disconnect(this._focusActorChangedId);
+ this._focusActorChangedId = 0;
+ }
+
+ this._focused = false;
+ return true;
+ },
+
+ _focusActorChanged: function() {
+ let focusedActor = global.stage.get_key_focus();
+ if (!focusedActor || !this._actor.contains(focusedActor))
+ this._focusUngrabbed();
+ },
+
+ ungrabFocus: function() {
+ if (!this._focusUngrabbed())
+ return;
+
+ if (this._prevKeyFocusActor) {
+ global.stage.set_key_focus(this._prevKeyFocusActor);
+ this._prevKeyFocusActor = null;
+ } else {
+ let focusedActor = global.stage.get_key_focus();
+ if (focusedActor && this._actor.contains(focusedActor))
+ global.stage.set_key_focus(null);
+ }
+ }
+});
+
const URLHighlighter = new Lang.Class({
Name: 'URLHighlighter',
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
this._notificationWidget.add_actor(this._notificationBin);
this._notificationWidget.hide();
+ this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
this._notificationQueue = [];
this._notification = null;
this._notificationClickedId = 0;
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
this._grabHelper.addActor(this._summaryBoxPointer.actor);
this._grabHelper.addActor(this.actor);
- this._grabHelper.addActor(this._notificationWidget);
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
},
_hideNotification: function() {
- // HACK!
- // There seems to be a reentrancy issue in calling .ungrab() here,
- // which causes _updateState to be called before _notificationState
- // becomes HIDING. That hides the notification again, nullifying the
- // object but not setting _notificationState (and that's the weird part)
- // As then _notificationState is stuck into SHOWN but _notification
- // is null, every new _updateState fails and the message tray is
- // lost forever.
- //
- // See more at https://bugzilla.gnome.org/show_bug.cgi?id=683986
- this._notificationState = State.HIDING;
-
- this._grabHelper.ungrab({ actor: this._notification.actor });
+ this._notificationFocusGrabber.ungrabFocus();
if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId);
},
_ensureNotificationFocused: function() {
- this._grabHelper.grab({ actor: this._notification.actor,
- grabFocus: true });
+ this._notificationFocusGrabber.grabFocus();
},
_onSourceDoneDisplayingContent: function(source, closeTray) {